package com.flannery.leetcode

import java.util.*

//https://www.runoob.com/w3cnote/sort-algorithm-summary.html
/**
 * 超级详细解读基本排序算法（不看后悔，带排序演示动画）
 * https://blog.csdn.net/litong09282039/article/details/46332127
 */
fun IntArray.forPrint(name: String) {
    val iterator = iterator()
    print("$name --> ")
    while (iterator.hasNext()) {
        print("${iterator.nextInt()} ,")
    }
    println()
}

fun IntArray.forPrintBefor(): IntArray {
    val iterator = iterator()
    while (iterator.hasNext()) {
        print("${iterator.nextInt()} ,")
    }
    return this
}


fun main() {
//    val mutableListOf = mutableListOf<Int>()
//    val random = Random()
//    for (i in 0..100) {
//        mutableListOf.add(random.nextInt(100))
//    }
//    mutableListOf.toIntArray().forPrintBefor()

    var intArrayOf = intArrayOf(13, 42, 20, 17, 14, 28, 15, 23)

    bubbleSort(intArrayOf.copyOf().forPrintBefor()).forPrint("冒泡排序")
    selectionSort(intArrayOf.copyOf().forPrintBefor()).forPrint("选择排序O(n2)")
    insertionSort(intArrayOf.copyOf().forPrintBefor()).forPrint("插入排序O(n2)")
    shellSort(intArrayOf.copyOf().forPrintBefor()).forPrint("希尔排序O(n1.5)")
    quickSort(intArrayOf.copyOf().forPrintBefor()).forPrint("快速排序O(N*logN)")
    mergeSort(intArrayOf.copyOf().forPrintBefor()).forPrint("归并排序O(N*logN)")
    heapSort(intArrayOf.copyOf().forPrintBefor()).forPrint("堆排序O(N*logN)")
    radixSort(intArrayOf.copyOf().forPrintBefor()).forPrint("基数排序 O(d(n+r))")
}

//冒泡排序	O(n2)
fun bubbleSort(intArray: IntArray): IntArray {
    var temp = 0
    for (aaa in 0 until intArray.size) {
        for (bbb in aaa until intArray.size) {
            if (intArray[aaa] > intArray[bbb]) {
                val temp = intArray[aaa]
                intArray[aaa] = intArray[bbb]
                intArray[bbb] = temp
            }
        }
    }
    return intArray
}

//选择排序	O(n2)
fun selectionSort(intArray: IntArray): IntArray {
    /*
    在长度为N的无序数组中，第一次遍历n-1个数，找到最小的数值与第一个元素交换；
    第二次遍历n-2个数，找到最小的数值与第二个元素交换；
    。。。
    第n-1次遍历，找到最小的数值与第n-1个元素交换，排序完成
     */
    for (index in intArray.indices) {
        for (jjj in index + 1 until intArray.size) {
            if (intArray[index] > intArray[jjj]) {
                val temp = intArray[index]
                intArray[index] = intArray[jjj]
                intArray[jjj] = temp
            }
        }
    }
    return intArray
}

//插入排序	O(n2)
fun insertionSort(intArray: IntArray): IntArray {
    /*
    在要排序的一组数中，假定前n-1个数已经排好序，现在将第n个数插到前面的有序数列中
    ，使得这n个数也是排好顺序的。如此反复循环，直到全部排好顺序。
     */
    for (indexAAA in 1 until intArray.size) {
        // 将这个数插入到前面去
        for (j in indexAAA downTo 1) {
            if (intArray[j] < intArray[j - 1]) {
                // 交换
                val temp = intArray[j]
                intArray[j] = intArray[j - 1]
                intArray[j - 1] = temp
            }
        }
    }

    return intArray
}

fun printlnShell(msg: String) {
    println(msg)
}

//希尔排序	O(n1.5)
fun shellSort(intArray: IntArray): IntArray {
    /*
    前言：
        数据序列1： 13-17-20-42-28 利用插入排序，13-17-20-28-42. Number of swap:1;
        数据序列2： 13-17-20-42-14 利用插入排序，13-14-17-20-42. Number of swap:3;
        如果数据序列基本有序，使用插入排序会更加高效。

    基本思想：
        在要排序的一组数中，根据某一增量分为若干子序列，并对子序列分别进行插入排序。
        然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。
     */


    //shell_sort(intArray, intArray.size)
    var incre = intArray.size
    while (incre > 1) {
        incre = incre.div(2)
        for (k in 0 until incre) { //得到所有的数
            // 遍历所有的分组
            for (j in (k + incre) until intArray.size step incre) { // 所有的分类
                for (i in j downTo k + 1 step incre) {
                    // 假设前面是有序
                    if (intArray[i] < intArray[i - incre]) {
                        val temp = intArray[i - incre];
                        intArray[i - incre] = intArray[i];
                        intArray[i] = temp;
                    } else {
                        break;
                    }
                }
            }
        }
    }
//    shell_sort(intArray, intArray.size)
    return intArray
}

fun shell_sort(array: IntArray, lenth: Int) {
    var temp = 0
    var incre = lenth
    while (true) {
        incre = incre / 2
        for (k in 0 until incre) {    //根据增量分为若干子序列
            var i = k + incre
            while (i < lenth) {
                var j = i
                while (j > k) {
                    if (array[j] < array[j - incre]) {
                        temp = array[j - incre]
                        array[j - incre] = array[j]
                        array[j] = temp
                    } else {
                        break
                    }
                    j -= incre
                }
                i += incre
            }
        }
        if (incre == 1) {
            break
        }
    }
}

/*
基本思想：（分治）

先从数列中取出一个数作为key值；
将比这个数小的数全部放在它的左边，大于或等于它的数全部放在它的右边；
对左右两个小数列重复第二步，直至各区间只有1个数。
辅助理解：挖坑填数

初始时 i = 0; j = 9; key=72
由于已经将a[0]中的数保存到key中，可以理解成在数组a[0]上挖了个坑，可以将其它数据填充到这来。
从j开始向前找一个比key小的数。当j=8，符合条件，a[0] = a[8] ; i++ ; 将a[8]挖出再填到上一个坑a[0]中。
这样一个坑a[0]就被搞定了，但又形成了一个新坑a[8]，这怎么办了？简单，再找数字来填a[8]这个坑。
这次从i开始向后找一个大于key的数，当i=3，符合条件，a[8] = a[3] ; j-- ; 将a[3]挖出再填到上一个坑中。
数组：72 - 6 - 57 - 88 - 60 - 42 - 83 - 73 - 48 - 85
 0   1   2    3    4    5    6    7    8    9
此时 i = 3; j = 7; key=72
再重复上面的步骤，先从后向前找，再从前向后找。
从j开始向前找，当j=5，符合条件，将a[5]挖出填到上一个坑中，a[3] = a[5]; i++;
从i开始向后找，当i=5时，由于i==j退出。
此时，i = j = 5，而a[5]刚好又是上次挖的坑，因此将key填入a[5]。
数组：48 - 6 - 57 - 88 - 60 - 42 - 83 - 73 - 88 - 85
 0   1   2    3    4    5    6    7    8    9
可以看出a[5]前面的数字都小于它，a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。
<数组：48 - 6 - 57 - 42 - 60 - 72 - 83 - 73 - 88 - 85
 0   1   2    3    4    5    6    7    8    9
平均时间复杂度：O(N*logN)
 */
//快速排序	O(N*logN)
fun quickSort(s: IntArray, l: Int = 0, r: Int = s.size - 1): IntArray {
    if (l < r) {
        var i = l
        var j = r
        var x = s[l] //挖坑，每次选取最左边
        while (i < j) {
            // 坑在左边， 从右边找到第一个比他小的值
            while (i < j && s[j] >= x) j--
            if (i < j) { // 符合要求
                //填坑
                s[i] = s[j]
                i++
            }

            // 此时坑在右边， 从左往右找到第一个比他大的值
            while (i < j && s[i] < x) i++
            if (i < j) {
                s[j] = s[i]
            }
        }
        // 中间的值赋值
        s[i] = x
        quickSort(s, l, i - 1)
        quickSort(s, i + 1, r)
    }
    return s
}

//归并排序	O(N*logN)
fun mergeSort(intArray: IntArray): IntArray {

    return intArray
}

//堆排序	O(N*logN)
fun heapSort(intArray: IntArray): IntArray {

    return intArray
}

//基数排序	O(d(n+r))
fun radixSort(intArray: IntArray): IntArray {

    return intArray
}